﻿namespace Tautologie
{
    class Tree
    {
        static TruthTable Table;
        Operation Root;
        string Expression;
        const string OPERATORS = "ANKC";

        public Tree(TruthTable T, string exp)
        {
            Table = T;
            if (exp == "") throw new EmptyExpressionException("Pole wprowadzania jest puste!");
            else
            {
                Expression = exp;
                FillTree();
            }
        }

        void FillTree()
        {
            if (!(IsOperator(Expression[0]))) throw new InvalidExpressionException("Nieznany operator lub argument. Sprawdź wyrażenie oraz sprawdź," +
                    " czy podałeś poprawną liczbę zdań i spróbuj ponownie.");
            Root = new Operator(Expression[0]);
            Operation Current = Root;
            for (int i = 1; i < Expression.Length; i++)
            {
                if (IsOperator(Expression[i]))
                {
                    while (true)
                    {
                        if (Current.Left == null)
                        {
                            Current.Left = new Operator(Expression[i]);
                            Current.Left.Previous = Current;
                            Current = Current.Left;
                            break;
                        }
                        else if (Current.Right == null)
                        {
                            Current.Right = new Operator(Expression[i]);
                            Current.Right.Previous = Current;
                            Current = Current.Right;
                            break;
                        }
                        else Current = Current.Previous;
                    }
                }
                else if (Table.IsSentence(Expression[i]))
                {
                    while (true)
                    {
                        if (Current.Left == null)
                        {
                            Current.Left = new Operand(Expression[i]);
                            break;
                        }
                        else if (Current.Right == null)
                        {
                            Current.Right = new Operand(Expression[i]);
                            break;
                        }
                        else Current = Current.Previous;
                    }
                }
                else throw new InvalidExpressionException("Nieznany operator lub argument. Sprawdź wyrażenie oraz sprawdź," + 
                    " czy podałeś poprawną liczbę zdań i spróbuj ponownie.");
                //throw if character in string is neither operator, nor sentence
            }
            if (CheckValidity(Root)) Proceed(Root); //called once filling tree is finished and validity checked
            else throw new TooLowOperandsException("Błędne wyrażenie. Wprowadzono zbyt małą liczbę operandów");
        }

        void Proceed(Operation op)
        {
            if (op.Left is Operator) Proceed(op.Left);
            if (op.Left is Operand && op.Right is NegationNullReference) //true only when negation
            {
                if (op == op.Previous.Left)
                {
                    op.Previous.Left = new Operand(op);
                }
                else
                {
                    op.Previous.Right = new Operand(op);
                }
            }
            if (op.Right is Operator) Proceed(op.Right);
            if (op.Left is Operand && op.Right is Operand)
            {
                if (op == Root)
                {
                    new Operand(op);
                }
                else if (op == op.Previous.Left)
                {
                    op.Previous.Left = new Operand(op);
                }
                else
                {
                    op.Previous.Right = new Operand(op);
                }
            }
        }

        bool CheckValidity(Operation op)
        {
            if (op.Left != null) CheckValidity(op.Left);
            else return false;
            if (op.Right != null) CheckValidity(op.Right);
            else return false;
            return true;
            //check if there's any operator which son's is null
        }

        abstract class Operation
        {
            public Operation Left;
            public Operation Right;
            public Operation Previous;
            public char OperationType;
        }

        class Operator : Operation
        {
            public Operator(char sentence)
            {
                OperationType = sentence;
                if (OperationType == 'N') Right = new NegationNullReference(); //if negation, rigth reference must be assigned with random,
                //no operator and no operand object;
            }
        }

        class NegationNullReference : Operation { }

        class Operand : Operation
        {
            string Sentence;
            int Position;

            public Operand(char sentence)
            {
                Sentence = sentence.ToString();
            }

            public Operand(Operation op)
            {
                if (op.OperationType == 'N')
                {
                    int index1 = Table.FindIndex(((Operand)op.Left).Sentence);
                    Position = Table.AddNegation(index1);
                    Sentence = "NEG( " + ((Operand)op.Left).Sentence + " )";
                    Table.AddLabel(Sentence);
                }
                else
                {
                    int index1 = Table.FindIndex(((Operand)op.Left).Sentence);
                    int index2 = Table.FindIndex(((Operand)op.Right).Sentence);
                    switch (op.OperationType)
                    {
                        case 'A':
                            Position = Table.AddDisjuncion(index1, index2);
                            Sentence = "DIS( " + ((Operand)op.Left).Sentence + ((Operand)op.Right).Sentence + " )";
                            break;
                        case 'K':
                            Position = Table.AddConjuction(index1, index2);
                            Sentence = "CON( " + ((Operand)op.Left).Sentence + ((Operand)op.Right).Sentence + " )";
                            break;
                        case 'C':
                            Position = Table.AddImplication(index1, index2);
                            Sentence = "IMP( " + ((Operand)op.Left).Sentence + ((Operand)op.Right).Sentence + " )";
                            break;
                    }
                    Table.AddLabel(Sentence);
                }
            }
        }
        bool IsOperator(char c)
        {
            //check if argument is defined logical operator 
            for (int i = 0; i < OPERATORS.Length; i++)
            {
                if (c == OPERATORS[i]) return true;
            }
            return false;
        }
    }
}
